home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Ham Radio 2000
/
Ham Radio 2000.iso
/
ham2000
/
tcp_ip
/
os2
/
pmnos11s
/
buckbook.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-07-30
|
24KB
|
884 lines
/*
Fred Peachman KB7YW
7186 Northview Drive
Brookfield, OH 44403
Sun 03-01-1992
This is a substitute for N4PCR's callsign server. The intent is to replace
N4PCR's callbook.c with this file - and have a callsign server that will
run off Buckmaster's CD-ROM: the October 1991 "HAM CALL" CD.
The CD-ROM must be configured with a driver installed in config.sys,
and the mscdex.exe TSR must be running.
The callserver database commands will configure the cd-rom.
config.h must #define CALLCLI or CALLSERVER. If CALLSERVER is #define'd
CALLCLI is forced on by config.h. Only CALLCLI can be #define'd separately
of CALLSERVER so it is the default condition for compilation of this
module - which won't used at all if neither CALLCLI nor CALLSERVER are
#define'd in config.h.
*/
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <sys/timeb.h>
#include "global.h"
#include "files.h"
#include <stdlib.h>
#include <dos.h>
#include <alloc.h>
#include <ctype.h>
#include <fcntl.h>
#include <io.h>
#include "cmdparse.h"
#include "config.h"
#ifdef CALLSERVER
/* CD-ROM code by Fred Peachman KB7YW */
char *CDROM = NULLCHAR;
#endif
char *Callserver = NULLCHAR;
/*struct database_activity
One of these structs gets filled out by init -
initializes data base for calls from hamcall.c -
should be able to handle generic functions across different data base
formats.
*/
struct database_activity {
int type; /* for checking data/function formats*/
#define USA 0
#define CANADA 1
int handle; /* -1 if unitialized */
unsigned long record; /* current record # */
void *record_buf; /* where to read the record to */
};
struct months {
char *m; /* name of month */
int d; /* days in month */
};
/* The Buckmaster HAM CALL CD format for U.S. Callsign database */
struct usa {
char callsign[6]; /* None of these fields are ASCIIZ so be careful */
char lastname[24];
char firstname[11];
char middle[1];
char street[35];
char city[20];
char state[2];
char zip[5];
char fluff[8]; /* supreme nothingness */
char exp[2]; /* 'X3' if license expired */
char born[2]; /* Last two digits of year of birth i.e. '54 */
char expiryr[2]; /* Last two digits of year of license expiration */
char expirday[3]; /* The day of the year that license expires */
char validyr[2]; /* year that license was issued */
char validday[3]; /* day of the year that license was issued */
char class[1]; /* 1 letter license classification */
char endline[2]; /* file marker */
};
/* The Buckmaster HAM CALL CD format for Canadian Callsign database
It is 0x9a bytes long (154) The first record has a blank callsign field
that should be no problem! */
struct canada {
char callsign[8];
char name[0xe8 - 0xa2];
char street[0x10a - 0xe8];
char town[0x121-0x10a];
char province[0x12c-0x121];
char zip[6];
char endline[2];
};
/* generic function calls: headers */
static int
init(struct database_activity *db,int type,int s);
static int
close_down(struct database_activity *db);
static void
disp(char *cfield, int siz, int s);
static int
readrec(struct database_activity *db);
static int
displayrec(struct database_activity *db, int s);
static int
find_call(struct database_activity *db, char *callsign);
static int
make_mask(char *mask, char *callsign, int s);
static int
docallserver_hostname(int argc,char *argv[],void *p);
static int
docallserver_databases(int argc,char *argv[],void *p);
/* for u.s.a database: */
static int
display_usa(void *,int s);
static int
comp_usa_calls(char *, char *);
/* for canada database */
static int
display_canada(void *,int s);
static int
comp_canada_calls(char *, char *);
/* most of this module is server code, but some is client: CALLCLI is by
default - forced on in config.h whenever CALLSERVER is #define'd */
#ifdef CALLSERVER
/* How many different databases are we supporting right now? */
#define DATABASE_TYPES 2
/* how many passes should we make before we give up (in find_call)? */
#define MAXREADS 30
/*----------------------------------------------------------------------------
Here are arrays of variables to use depending on which database is
needed. In each array make sure the value order is same as defs for
database_activity.types i.e. USA, CANADA, ....
In order to tack on another database, add an element for each of these
arrays, increment the definition for DATABASE_TYPES (above), and
tell your program how to tell init to use the new one. In this
module the launcher (cb_lookup) will figure out which database to
initialize ("init(type, callsign,s)").
Basically for the new database you would only need to specify its file
name in database[], write a display function and specify it in
displayfunc[], specify the size of one record in record_size[],
write a callsign comparison function and specify it in compare_calls[],
and specify the byte offset of the callsign field within a record -
specify this offset in call_offset[] - it will probably be 0 anyway.
You need to write something in cb_lookup to tell init when to initialize
your new database. Basically if "call" satisfies inspection, initialize
the appropriate database.
*/
char *database[DATABASE_TYPES] = {
{ NULLCHAR }, /* "S:\\HAM0\\HAMCALL.129"}, USA */
{ NULLCHAR } /* "S:\\HAM0\\CANADA2.DAT"} CANADA */
};
char *database_label[DATABASE_TYPES] = {
{ "usa"},{"canadian"}
};
int (*displayfunc[DATABASE_TYPES])(void *, int) = {
{ display_usa },
{ display_canada }
};
/* basic record size, in bytes, in each database */
int record_size[DATABASE_TYPES] = { 129, 154 };
/* How fresh is this stuff in the database? */
static char *Volume = "October 1991";
/* number of records in each type of database */
unsigned long records[DATABASE_TYPES];
/* The database-specific callsign comparison procedure */
int (*compare_calls[DATABASE_TYPES])(char *,char *) = {
{comp_usa_calls},{comp_canada_calls}
};
/* At what offset within a database struct is the callsign located?
Look at struct usa and struct canada above
*/
int call_offset[DATABASE_TYPES] = { 0,0 };
char *BusyServer = "\n\nThe callsign server is busy right now\n\
.... please call back later\n\n";
static char *TAB = " ";
/* for use in display_usa */
struct months month[12] = {
{"January", 31},{"February",28},{"March",31},{"April",30},{"May",31},
{"June",30},{"July",31},{"August",31},{"September",30},{"October",31},
{"November",30},{"December",31}
};
/*----------------------------------------------------------------------------
/* --------------- Generic (database-independent) functions ----------------*/
/*----------------------------------------------------------------------------
Initialize a database activity struct. this consists of making sure
the database file is opened and that the number of records in that
file is computed. Also a record_buf is malloced for each db.
*/
static int
init(db, type, s)
struct database_activity *db;
int type;
int s;
{
long tmp;
int i;
extern char *Callserver; /* declared in calldbd.c */
if ((type < 0) || (type >= DATABASE_TYPES)) return -1;
db->type = type;
i = db->handle = _open(database[type], O_RDONLY | O_BINARY);
if (i == -1) {
usprintf(s,"%s",BusyServer);
return 0;
}
for (i = 0; i < 50; i++) pwait(NULL);
/* The CD-ROM is a slow device!! - give other processes lots of slack */
tmp = filelength(db->handle);
/* if (tmp == -1L) {
usprintf(s,"Error reading length of file \"%s\"\n\
please report to the sysop at %s\n",database[type], Callserver);
return -1;
}
*/
records[type] = tmp/((long) record_size[type]);
db->record_buf = (char *)malloc(record_size[type]);
if (db->record_buf == NULL) {
usprintf(s,"%s",BusyServer); /* No memory to allocate, actually */
return -1;
}
return 0;
}
static int
close_down(db)
struct database_activity* db;
{
free(db->record_buf);
_close(db->handle);
}
static int
invalid_callsign(callsign, s)
char *callsign;
int s;
{
usprintf(s,"\n\n\"%s\" is an invalid callsign\n\n", callsign);
return -1;
}
/* make_mask makes a Buckmaster-compatible mask out of the callsign.
caller must allocate 6 bytes exactly for "mask" or 7 to make an ascii
string out of it.
callsign is the callsign we are looking to match; mask is the same
callsign only padded to Left with a space if necessary to keep the
callsign digit at position 2 (starting with 0), and padded with spaces
to the right if necessary to make sure the mask is exactly 6 digits
long.
This routine will work right for US or Canadian callsigns, but NOT with
foreign callsigns (that contain more than one numeric character) it
will fail!
*/
static int
make_mask(mask, callsign,s)
char *mask;
char *callsign;
int s;
{
int j, k, len, already=0;
char *q, *p;
char tmp[7];
len = strlen(callsign);
/* there must be only ONE digit-character at position 1 or 2
(starting from 0) */
q = callsign;
already = 0;
for (j = 0; j < len; j++) {
if (isdigit(*q++)) {
if (!(already++)) k = j;/* already counts the number of digit chars */
else return invalid_callsign(callsign,s);
}
}
if ((k < 1) || ( k > 2)) return invalid_callsign(callsign,s);
/* FINALLY, Buckmaster pads to the left with spaces to position the digit at
mask[2] */
q = mask;
for (j = 0; j < 6; j++) *q++ = ' '; /* initialize! */
p = callsign;
q = mask;
switch (k) {
case 1:
*q++ = ' '; /* left-pad with a space */
break;
case 2:
break;
default:
break;
/* usprintf(s,"Error in make_mask %s line %d\n",__FILE__,__LINE__);
exit(255);
*/
}
for (j = 0; j < len; j++) *q++ = *p++;
return ((int) (mask[2]) &0x0f); /* returns digit of the valid U.S. callsign */
}
/* a method for display of non-ASCIIZ strings */
static void
disp(cfield,siz,s)
char *cfield;
int siz;
int s;
{
char *q;
int i, sw=0;
q = cfield;
for (i = 0; i < siz; i++) {
if ((!sw) || (*q != ' ')) usputc(s, (int) *q, stdout);
if (*q == ' ') sw = 1;
else sw = 0; /* We will print out one space before we quit! */
q++;
}
}
static int
readrec(db)
struct database_activity *db;
{
unsigned long tmp, filepointer;
size_t n;
int st;
st = db->type;
filepointer = db->record*((long) record_size[st]);
tmp = lseek(db->handle, filepointer,SEEK_SET);
/*
if (tmp != filepointer) {
usprintf(s,"filepointer moved to %08lu\n", tmp);
return -1;
}
*/
n = _read(db->handle, db->record_buf,record_size[st]);
/*
if (n != record_size[st]) {
usprintf(s,"Error reading database\n");
return -1;
}
else
*/
return 0;
}
static int
displayrec(db,s)
struct database_activity *db;
int s;
{
int (*func)(void *v, int s);
func = displayfunc[db->type];
usprintf(s,"\n%s*** Record # % 8lu:***\n",TAB, db->record);
func(db->record_buf,s);
return 0;
}
/* find_call: Find the record number that matches the callsign. For any
callbook data where the records are all of the same length. The
returned record value is found as db->record.
If the input callsign needs to be converted to a search mask in any
way, do it then call findcall.
The returned int value is the number of records read during the search
process.
*/
static int
find_call(db, callsign)
struct database_activity *db;
char *callsign;
{
int i, ret, met= 0, reads = 0;
long ra,rz; /* first, middle, last record numbers */
int (*comp)(char *, char *);
int st;
char *rec_call;
st = db->type; /* determine database structure type */
comp = compare_calls[st];
/* set initial pointers */
ra = 0L;
rz = records[st] - 1L;
db->record = (ra + rz)/2L; /* go for the middle */
rec_call = (char *)db->record_buf + call_offset[st];
ret = 1;
while (ret != 0) {
readrec(db); /* read in the data pointed to by db->record */
for (i=0; i < 50; i++)
pwait(NULL); /* sit back and let other processes run for a while.
The CD-ROM is a slow-access device so lots of other processes may
be waiting or trying to run - like the keyboard!!! */
reads++;
ret = comp(callsign, rec_call);
if (ret < 0) /* callsign < record's callsign field */
rz = db->record - 1L;
else if (ret > 0) /* callsign > record's callsign field */
ra = db->record + 1L;
else return reads;
if (reads == MAXREADS) return -1; /* Abnormal return */
else if (ra >= rz) {
if (met) return -1;
else met = 1; /* catch this if it happens a second time! */
}
db->record = (ra + rz)/2L;
}
/* not reached */
}
/*---------------- ROUTINES FOR U.S. CALLSIGN DATABASE ----------------------*/
/* getdaynum: given a character array 'p' of 'siz' elements, convert the
array to an ASCIIZ string, return the integer value that it
represents.
*/
static int
getdaynum(p,siz)
char *p;
int siz;
{
char *q, tmp[80];
int ret, i;
q = p;
for (i = 0; i < siz; i++) {
tmp[i] = *q++;
}
tmp[i] = '\0';
ret = atoi(tmp);
return ret;
}
/* yrval:
Take the two-letter year code and determine if we have a year that is in
the 1900's or the 21st century. Return a proper integer answer:
i.e. "2002" or "1998".
*/
static int
yrval(yrin)
char *yrin;
{
int i, num;
num = getdaynum(yrin, 2);
if (num > 90) num += 1900;
else num += 2000;
return num; /* an integer */
}
/* day_calc: given the 3-element character array 'day' and the 2-element
character array 'yr', construct a string that returns the date for
that year, in the form "February 28, 2002" */
static char *
day_calc(day, daystr, yr)
char day[3];
char *daystr;
char yr[2];
{
int i, daynum, subt, year, t;
char *q, tmp[4];
int leap = 0;
year = yrval(yr);
if (!(year%4)) leap = 1; /* evenly divisible by four means leap year */
daynum = getdaynum(day, 3);
if (daynum == 0) return NULL;
subt = 0;
for (i = 0; i < 12; i++) {
if (((t = subt + ((i == 1) ? leap : 0) + month[i].d)) > daynum) break;
else (subt = t);
}
t = daynum - subt;
if (i == 12) daystr[0] = '\0';
else sprintf(daystr,"%s %d, %d", (t > 0 ? month[i].m : month[--i].m),
(t > 0 ? t : month[i].d + ((i==1) ? leap : 0)),
year);
return daystr;
}
/* comp_usa_calls compares the sought-for callsign with a mask of the
type encountered in the callsign field of a struct usa,
returns 0 if they match, -1 if mask1 < mask2, 1 if mask1 > mask2.
mask1 is the callsign requested, mask2 is the database callsign from
the record under test for match.
*/
static int
comp_usa_calls(mask1, mask2)
char mask1[6];
char mask2[6];
{
int i;
char *p, *q;
/* This is in accordance with the way that Buckmaster orders callsigns
on its CD-ROM database, so follow along:
*/
p = &mask1[2], q = &mask2[2];
for (i =2; i < 6; i++) {
if (*p != *q) return (int) *p - (int) *q;
p++, q++;
}
p = mask1, q = mask2;
for (i =0; i < 2; i++) {
if (*p != *q) return (int) *p - (int) *q;
p++, q++;
}
return 0;
}
/*
Buckmaster CD-ROM October 1991
*** Record # 452100:***
Call: KB7YW
FREDERICK A PEACHMAN
7186 NORTHVIEW DR
BROOKFIELD , OH 44403
Born: 1954
Class: EXTRA
Exp: March 29, 1998
*/
static int
display_usa(us,s)
void *us;
int s;
{
struct usa *u;
char daystr[20];
int yeardate;
u = (struct usa *)us;
usprintf(s, "CALL: ");
disp(u->callsign,sizeof(u->callsign),s);
usprintf(s,"\n%s", TAB);
disp(u->firstname,sizeof(u->firstname),s);
disp(u->middle,sizeof(u->middle),s);
usprintf(s," ");
disp(u->lastname,sizeof(u->lastname),s);
usprintf(s,"\n%s", TAB);
disp(u->street,sizeof(u->street),s);
usprintf(s,"\n%s", TAB);
disp(u->city,sizeof(u->city),s);
usprintf(s,", ");
disp(u->state,sizeof(u->state),s);
usprintf(s," ");
disp(u->zip,sizeof(u->zip),s);
if (u->born[0] != '-') {
usprintf(s,"\nBorn: 19");
disp(u->born,sizeof(u->born),s);
}
usprintf(s,"\nClass: ");
switch(u->class[0]) {
case 'N':
usprintf(s,"NOVICE");
break;
case 'T':
usprintf(s,"TECHNICIAN");
break;
case 'C':
usprintf(s, "CLUB STATION");
break;
case 'G':
usprintf(s,"GENERAL");
break;
case 'A':
usprintf(s,"ADVANCED");
break;
case 'E':
usprintf(s,"EXTRA");
break;
default:
usprintf(s,"%c", u->class[0]);
}
usprintf(s,"\n");
yeardate = yrval(u->expiryr);
if (day_calc(u->expirday, daystr, u->expiryr) != NULL) {
if (daystr[0] != '\0') {
if ((u->exp[0] == ' ') || (yeardate > 2000)) {
usprintf(s,"Expires:");
usprintf(s,"%s\n", daystr);
}
else usprintf(s,"%sUnknown Expiration Date\n",TAB);
}
else usprintf(s,"%sUnknown Expiration Date\n",TAB);
}
return 0;
}
/*---------- Procedures specific to Canadian Callsign DataBase----------------*/
static int
display_canada(us,s)
void *us;
int s;
{
struct canada *u;
#define DISP(x) {usprintf(s,"\n ");\
disp(x, sizeof(x),s);}
u = (struct canada *) us;
DISP(u->callsign);
DISP(u->name);
DISP(u->street);
DISP(u->town);
disp(u->zip, sizeof(u->zip),s);
usprintf(s,"\n");
usprintf(s,"%sCANADA\n",TAB);
return 0;
}
/* comp_canada_calls compares the sought-for callsign with a mask of the
type encountered in the callsign field of a struct canada,
returns 0 if they match, -1 if mask1 < mask2, 1 if mask1 > mask2.
mask1 is the callsign requested, mask2 is the database callsign from
the record under test for match.
*/
static int
comp_canada_calls(mask1, mask2)
char mask1[6];
char mask2[6];
{
int i;
char *p, *q;
p = mask1, q = mask2;
for (i =0; i < 6; i++) {
if (*p != *q) return (int) *p - (int) *q;
p++, q++;
}
return 0;
}
/* One of three public functions! */
int
cb_lookup(s,call)
int s;
char *call;
{
/* This is the launcher. Get rid of any callsign validity-checking code
that happens before calling cb_lookup. Eventually we will have to
launch the US, Canadian, or foreign Buckmaster CD databases. For right
now, tho, we only have USA and CANADA.
*/
char *q;
int len, j;
char mask[7];
struct database_activity DA;
char *cant = "Can't handle callsign \"%s\"\n";
/* FIRST TEST CALLSIGN FOR VALIDITY */
/* Buckmaster only matches callsigns of 4 to 6 letters in length */
len = strlen(call);
if ((len < 4) || (len > 6)) return invalid_callsign(call,s);
for (j = 0; j < 6; j++) call[j] = toupper(call[j]);
/* every character must be alphanumeric */
q = call;
for (j = 0; j < len; j++) if (!(isalnum(*q++)))
return invalid_callsign(call,s);
q = call;
switch (*q) {
case 'A':
case 'K':
case 'N':
case 'W':
if ((init(&DA, USA, s)) == -1) return 0;
if ((make_mask(mask, call, s)) == -1) {
close_down(&DA);
return 0;
}
break;
case 'V':
if ((strncmp(call,"VO",2) == 0) || (strncmp(call,"VE",2) == 0)) {
if ((init(&DA, CANADA, s)) == -1) return 0;
if ((make_mask(mask, call, s)) == -1) {
close_down(&DA);
return 0;
}
}
else {
usprintf(s,cant,call);
return 0;
}
break;
default:
usprintf(s,cant,call);
return 0;
}
if ((find_call(&DA,mask)) == -1)
usprintf(s, "\n\nCallsign \"%s\" not found\n\n",call);
else {
displayrec(&DA,s);
usprintf(s,"\n%sDatabase date: %s\n\n\n",TAB, Volume);
}
/* find_call returns with appropriate record already read into
DA.record_buf and the return value is -1 for error or number of
seeks required on success.
*/
close_down(&DA);
return 0;
}
#endif /* #ifdef CALLSERVER */
struct cmds Callserver_cmds[] = {
"hostname", docallserver_hostname, 0, 0, NULLCHAR,
#ifdef CALLSERVER
"database", docallserver_databases, 0, 0, NULLCHAR,
#endif
NULLCHAR,
};
/* Another public, headered in commands.h */
int
docallserver(argc,argv,p)
int argc;
char *argv[];
void *p;
{
return subcmd(Callserver_cmds, argc, argv, p);
}
static int
docallserver_hostname(argc,argv,p)
int argc;
char *argv[];
void *p;
{
if(argc < 2)
{
if(Callserver != NULLCHAR)
tprintf("The callserver's hostname is: %s\n",Callserver);
else
{
tprintf("Callserver not configured!\n");
tprintf("Usage: callserver <hostname>|<ip_address>\n");
}
}
else {
if(Callserver != NULLCHAR)
free(Callserver);
Callserver = strdup(argv[1]);
}
return 0;
}
#ifdef CALLSERVER
static int
docallserver_databases(argc,argv,p)
int argc;
char *argv[];
void *p;
{
int t;
if (argc < 2) {
for (t = 0; t < DATABASE_TYPES; t++) {
tprintf("callserver database %s ", database_label[t]);
if (database[t] == NULLCHAR) {
tprintf("not configured.\n");
}
else tprintf("%s\n", database[t]);
}
return 0;
} else { /* argc >= 2 */
for (t = 0; t < DATABASE_TYPES; t++) {
if (strncmp(database_label[t],argv[1],strlen(argv[1])) == 0) {
if (argc < 3) {
tprintf("callserver database %s ", database_label[t]);
if (database[t] == NULLCHAR) tprintf("not configured\n");
else tprintf("%s\n", database[t]);
} else { /* argc >= 3 */
if (database[t] == NULLCHAR) free(database[t]);
database[t] = strdup(argv[2]);
}
return 0; /* we have found our match with database_label[t] */
}
}
/* no match with database_label: which database? */
tprintf("Usage: callserver database ");
for (t = 0; t < DATABASE_TYPES; t++) {
tprintf("%s", database_label[t]);
if ( t < DATABASE_TYPES - 1) tprintf("|");
}
tprintf(" <database drive:/path/filename>\n");
return -1;
}
/* not reached */
}
/* the last public of this module, also headered in commands.h */
int
docdrom(argc,argv,p)
int argc;
char *argv[];
void *p;
{
extern char *CDROM; /* declared above, this module */
if (argc < 2) {
if (CDROM == NULLCHAR) {
tprintf("CDROM drive letter not specified\n");
}
else tprintf("CDROM drive is \"%s\"\n", CDROM);
return 0;
}
else {
if ((argv[1][2] == '\0') &&
(isalpha(argv[1][0])) &&
(argv[1][1] == ':')) {
if (CDROM != NULLCHAR) free(CDROM);
CDROM = strdup(argv[1]);
return 0;
}
else {
tprintf("Usage: \"cdrom driveletter:\", e.g. \"cdrom s:\"\n");
return 1;
}
}
/* not reached */
}
#endif /* #ifdef CALLSERVER */